﻿using System;
using System.ComponentModel;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace NewLife.Security
{
    /// <summary>数据助手</summary>
    public static class DataHelper
    {
        #region 散列
        /// <summary>MD5散列</summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static String Hash(String str)
        {
            //if (String.IsNullOrEmpty(str)) throw new ArgumentNullException("str");
            if (String.IsNullOrEmpty(str)) return null;

            var md5 = new MD5CryptoServiceProvider();
            var by = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
            return BitConverter.ToString(by).Replace("-", "");
        }

        /// <summary>文件散列</summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static String HashFile(String filename)
        {
            if (String.IsNullOrEmpty(filename)) throw new ArgumentNullException("filename");

            var md5 = new MD5CryptoServiceProvider();
            //Byte[] buffer = md5.ComputeHash(File.ReadAllBytes(filename));
            using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
            {
                var buffer = md5.ComputeHash(stream);
                return BitConverter.ToString(buffer).Replace("-", "");
            }
        }
        #endregion

        #region TripleDES加解密
        private static SymmetricAlgorithm GetProvider(String key)
        {
            var sa = new TripleDESCryptoServiceProvider();
            Int32 max = sa.LegalKeySizes[0].MaxSize / 8;
            key = Hash(key);
            String str = key;
            Byte[] bts = Encoding.UTF8.GetBytes(str);

            if (bts.Length != max) Array.Resize<Byte>(ref bts, max);

            sa.Key = bts;

            max = sa.LegalBlockSizes[0].MaxSize / 8;
            bts = Encoding.UTF8.GetBytes(str);
            //倒序
            Array.Reverse(bts);
            if (bts.Length != max) Array.Resize<Byte>(ref bts, max);
            sa.IV = bts;

            return sa;
        }

        /// <summary>TripleDES加密</summary>
        /// <param name="content">UTD8编码的明文</param>
        /// <param name="key">密码字符串经MD5散列后作为DES密码</param>
        /// <returns></returns>
        public static String Encrypt(String content, String key)
        {
            if (String.IsNullOrEmpty(content)) throw new ArgumentNullException("content");
            var data = Encoding.UTF8.GetBytes(content);

            data = Encrypt(data, key);

            return Convert.ToBase64String(data);
        }

        /// <summary>TripleDES加密</summary>
        /// <param name="data"></param>
        /// <param name="key">密码字符串经MD5散列后作为DES密码</param>
        /// <returns></returns>
        public static Byte[] Encrypt(Byte[] data, String key)
        {
            if (data == null || data.Length < 1) throw new ArgumentNullException("data");
            if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key");

            var outstream = new MemoryStream();
            var stream = new CryptoStream(outstream, GetProvider(key).CreateEncryptor(), CryptoStreamMode.Write);
            stream.Write(data, 0, data.Length);
            stream.FlushFinalBlock();

            data = outstream.ToArray();

            stream.Close();
            outstream.Close();

            return data;
        }

        /// <summary>TripleDES解密</summary>
        /// <param name="content">UTD8编码的密文</param>
        /// <param name="key">密码字符串经MD5散列后作为DES密码</param>
        /// <returns></returns>
        public static String Descrypt(String content, String key)
        {
            if (String.IsNullOrEmpty(content)) throw new ArgumentNullException("content");
            Byte[] data = Convert.FromBase64String(content);

            data = Descrypt(data, key);

            return Encoding.UTF8.GetString(data);
        }

        /// <summary>TripleDES解密</summary>
        /// <param name="data"></param>
        /// <param name="key">密码字符串经MD5散列后作为DES密码</param>
        /// <returns></returns>
        public static Byte[] Descrypt(Byte[] data, String key)
        {
            if (data == null || data.Length < 1) throw new ArgumentNullException("data");
            if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key");

            var ms = new MemoryStream(data);
            var stream = new CryptoStream(ms, GetProvider(key).CreateDecryptor(), CryptoStreamMode.Read);

            var ms2 = new MemoryStream();
            while (true)
            {
                Byte[] buffer = new Byte[10];
                Int32 count = stream.Read(buffer, 0, buffer.Length);
                if (count <= 0) break;

                ms2.Write(buffer, 0, count);
                if (count < buffer.Length) break;
            }

            data = ms2.ToArray();

            stream.Close();
            ms.Close();
            ms2.Close();

            return data;
        }
        #endregion

        #region RC4加密
        /// <summary>RC4加密解密</summary>
        /// <param name="data">数据</param>
        /// <param name="pass">UTF8编码的密码</param>
        /// <returns></returns>
        public static Byte[] RC4(Byte[] data, String pass)
        {
            if (data == null || pass == null) return null;
            Byte[] output = new Byte[data.Length];
            Int64 i = 0;
            Int64 j = 0;
            Byte[] mBox = GetKey(Encoding.UTF8.GetBytes(pass), 256);

            // 加密
            for (Int64 offset = 0; offset < data.Length; offset++)
            {
                i = (i + 1) % mBox.Length;
                j = (j + mBox[i]) % mBox.Length;
                Byte temp = mBox[i];
                mBox[i] = mBox[j];
                mBox[j] = temp;
                Byte a = data[offset];
                //Byte b = mBox[(mBox[i] + mBox[j] % mBox.Length) % mBox.Length];
                // mBox[j] 一定比 mBox.Length 小，不需要在取模
                Byte b = mBox[(mBox[i] + mBox[j]) % mBox.Length];
                output[offset] = (Byte)((Int32)a ^ (Int32)b);
            }

            return output;
        }

        /// <summary>打乱密码</summary>
        /// <param name="pass">密码</param>
        /// <param name="kLen">密码箱长度</param>
        /// <returns>打乱后的密码</returns>
        static Byte[] GetKey(Byte[] pass, Int32 kLen)
        {
            Byte[] mBox = new Byte[kLen];

            for (Int64 i = 0; i < kLen; i++)
            {
                mBox[i] = (Byte)i;
            }
            Int64 j = 0;
            for (Int64 i = 0; i < kLen; i++)
            {
                j = (j + mBox[i] + pass[i % pass.Length]) % kLen;
                Byte temp = mBox[i];
                mBox[i] = mBox[j];
                mBox[j] = temp;
            }
            return mBox;
        }
        #endregion

        #region RSA签名
        /// <summary>签名</summary>
        /// <param name="data"></param>
        /// <param name="priKey"></param>
        /// <returns>Base64编码的签名</returns>
        internal static String Sign(Byte[] data, String priKey)
        {
            if (data == null | String.IsNullOrEmpty(priKey)) return null;

            var rsa = new RSACryptoServiceProvider();
            var md5 = new MD5CryptoServiceProvider();
            try
            {
                rsa.FromXmlString(priKey);
                return Convert.ToBase64String(rsa.SignHash(md5.ComputeHash(data), "1.2.840.113549.2.5"));
            }
            catch { return null; }
        }
        #endregion

        #region RSA验证签名
        /// <summary>验证签名</summary>
        /// <param name="data">待验证的数据</param>
        /// <param name="signdata">Base64编码的签名</param>
        /// <param name="pubKey">公钥</param>
        /// <returns></returns>
        internal static Boolean Verify(Byte[] data, String signdata, String pubKey)
        {
            if (data == null ||
                data.Length < 1 ||
                String.IsNullOrEmpty(signdata) ||
                String.IsNullOrEmpty(pubKey)) return false;

            var rsa = new RSACryptoServiceProvider();
            var md5 = new MD5CryptoServiceProvider();
            try
            {
                rsa.FromXmlString(pubKey);
                return rsa.VerifyHash(md5.ComputeHash(data), "1.2.840.113549.2.5", Convert.FromBase64String(signdata));
            }
            catch { return false; }
        }
        #endregion

        #region 编码
        ///// <summary>把字节数组编码为十六进制字符串</summary>
        ///// <param name="data"></param>
        ///// <param name="offset">偏移</param>
        ///// <param name="count">数量</param>
        ///// <returns></returns>
        //[Obsolete("=>IOHelper.ToHex")]
        //[EditorBrowsable(EditorBrowsableState.Never)]
        //public static String ToHex(this Byte[] data, Int32 offset = 0, Int32 count = 0) { return IOHelper.ToHex(data, offset, count); }

        /// <summary>把十六进制字符串解码字节数组</summary>
        /// <param name="data"></param>
        /// <param name="startIndex">起始位置</param>
        /// <param name="length">长度</param>
        /// <returns></returns>
        [Obsolete("=>IOHelper.ToHex")]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static Byte[] FromHex(String data, Int32 startIndex = 0, Int32 length = 0) { return IOHelper.ToHex(data, startIndex, length); }
        #endregion
    }
}